﻿using System;
using System.Collections.Generic;
using GPainter.Data;
using GPainter.Events;
using GPainter.Interfaces;
using Point = GPainter.Data.Point;

namespace GPainter.ModelViewPresenter
{
    //класс презентора - "тонкий" контроллер - делегирование бизнес-логики в пользовательскую сборку 
    public class Presenter
    {

        #region  События, на которые подписывается внешняя программа, использующая компонент

        public event EventHandler<NodeAddEventArgs> OnAddNewNode;//добавление новой вершины
        public event EventHandler<EdgeAddEventArgs> OnAddNewEdge;//добавление новой дуги
        public event EventHandler<NodeAddEventArgs> onSelectNode;//выделение вершины
        public event EventHandler<NodeDeleteEventArgs> onDeleteNode;//удаление вершины
        public event EventHandler<EdgeDeleteEventArgs> onDeleteEdge;//удаление дуги
        public event EventHandler<OperationEventArgs> OnChangeOperation;
        public event EventHandler<EventArgs> OnDeselectAll;

        #endregion



        private Model MainGraph = new Model();//модель
        private View _view;//ссылка на представление
        public string SelectedNodeId;//выделенная вершина
        //подпихиваем в конструктор презентера представление
        public Presenter(View view)
        {
            _view = view;
            //регистририруем обработчики событий
            _view.ChangeNodePosition += OnChangeNodePosition;
            //_view.NeedPlanarize += new EventHandler<EventArgs>(_view_NeedPlanarize);
            _view.AddNewNode += _view_AddNewNode;
           // _view.AddNewEdge += _view_AddNewEdge;
            _view.OnDeleteEdge += _view_DeleteEdge;
            _view.OnDeleteNode += _view_OnDeleteNode;
            _view.OnSelectNode += _view_OnSelectNode;
            _view.OnChangeOperation += _view_OnChangeOperation;
            _view.UpdateAllNodeEdges += _view_UpdateAllNodeEdges;
            _view.OnDeselectAll += _view_OnDeselectAll;
            _view.OnAutomaticLayout += _view_OnAutomaticLayout;
            //_view.SaveScheme += new EventHandler<FileEventArgs>(_view_SaveScheme);
            //_view.LoadScheme += new EventHandler<FileEventArgs>(_view_LoadScheme);
            SelectedNodeId = String.Empty;
        }
        

       
     //   IClassFactory classFactory;// класс фактори

        //List<Type> nodeTypes;//коллекция типов вершин
        //List<Type> edgeTypes;//коллекция типов дуг

        //private void Init(string XMLSchemeFileName,//семантика ПрО
        //                        string AssemblyNodeName,//имя сборки,в которой хранится класс вершины
        //                        string AssemblyEdgeName,//имя сборки,в которой хранится класс ребра
        //                        string AssemblyClassFactoryName//имя сборки, в которой хранится фабрика классов
        //                            )
        //{
        //    //грузим сборки через рефлексию ептиж)))
        //    Assembly asmNode = Assembly.LoadFrom(AssemblyNodeName);
        //    Assembly asmEdge;
        //    if (AssemblyNodeName != AssemblyEdgeName)
        //    {
        //        asmEdge = Assembly.LoadFrom(AssemblyEdgeName);
        //    }
        //    else
        //    {
        //        asmEdge = asmNode;
        //    }
        //    //получаем массивы типов
        //    Type[] EdgeTypes = asmEdge.GetTypes();
        //    Type[] NodeTypes = asmNode.GetTypes();
        //    //теперь создаём гетерогенные коллекции нужных нам типов
        //    nodeTypes = new List<Type>();
        //    edgeTypes = new List<Type>();
        //    //теперь посмотрим, какие классы реализуют соответственно интерфейсы INode и IEdgeData
        //    foreach (Type t in NodeTypes)
        //    {
        //        Type tInterface = t.GetInterface("INode");
        //        if (tInterface != null)
        //        {
        //            nodeTypes.Add(t);
        //        }
        //    }
        //    foreach (Type t in EdgeTypes)
        //    {
        //        Type tInterface = t.GetInterface("IEdgeData");
        //        if (tInterface != null)
        //        {
        //            edgeTypes.Add(t);
        //        }
        //    }
        //    //если нас надули - проверка на лоха
        //    if ((edgeTypes.Count == 0) || (nodeTypes.Count == 0))
        //    {
        //        MessageBox.Show("В сборках не найдены классы, реализующие  INode и IEdgeData!",
        //            "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
        //        return;
        //    }
        //    //проверка на наличие IClassFactory
        //    Assembly asmClFactory;
        //    if (AssemblyNodeName != AssemblyClassFactoryName)
        //    {
        //        asmClFactory = Assembly.LoadFrom(AssemblyClassFactoryName);
        //    }
        //    else
        //    {
        //        asmClFactory = asmNode;
        //    }
        //    Type[] tsClFactory = asmClFactory.GetTypes();
        //    Type IclFactMeta = null;//метаданные типа фабрики классов
        //    foreach (Type type in tsClFactory)
        //    {
        //        Type tClFact = type.GetInterface("IClassFactory");
        //        if (tClFact != null)
        //        {
        //            IclFactMeta = type;
        //            break;
        //        }
        //    }
        //    if (IclFactMeta == null)
        //    {
        //        MessageBox.Show("В сборке не найден класс, реализующий IClassFactory!",
        //            "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
        //        return;
        //    }
        //    classFactory = (IClassFactory)Activator.CreateInstance(IclFactMeta);
        //    //создаем xml-документ
        //    XmlDocument doc = new XmlDocument();
        //    if (XMLSchemeFileName != "")
        //    {
        //        doc.Load(XMLSchemeFileName);
        //        //заполняем модель данных через фабрику классов
        //        classFactory.Init(doc);
        //        MainGraph.Nodes = classFactory.CreateInstancesOfNodes();
        //        MainGraph.Edges = classFactory.CreateInstancesOfEdges();
        //    }
           
        //}

        ////инициализатор презентора
        //public void InitGraph(string XMLSchemeFileName,//семантика ПрО
        //                        string AssemblyNodeName,//имя сборки,в которой хранится класс вершины
        //                        string AssemblyEdgeName,//имя сборки,в которой хранится класс ребра
        //                        string AssemblyClassFactoryName//имя сборки, в которой хранится фабрика классов
        //                            )
        //{
        //    Init(XMLSchemeFileName, AssemblyNodeName, AssemblyEdgeName, AssemblyClassFactoryName);
        //}

        //public void InitGraph(string XMLSchemeFileName,//семантика ПрО
        //                       string AssemblyNodeName,//имя сборки,в которой хранится класс вершины
        //                       string AssemblyEdgeName,//имя сборки,в которой хранится класс ребра
        //                        string AssemblyClassFactoryName,
        //                        Point[] points // конкретные координаты вершин
        //                           )
        //{
        //    Init(XMLSchemeFileName, AssemblyNodeName, AssemblyEdgeName, AssemblyClassFactoryName);
        //    for (int i = 0; i < MainGraph.Nodes.Count;i++ )
        //    {
        //        MainGraph.Nodes[i].Pos = points[i];
        //    }
        //}

        ////облегченный вариант загрузки без XML и все классы в одной DLL
        //public void InitGraph(string graphClassesAssemblyName)
        //{
        //    Init("", graphClassesAssemblyName, graphClassesAssemblyName, graphClassesAssemblyName);
        //}

        ////заглушка
        //public void InitGraph()
        //{
        //   classFactory = new ClassFactory();
        //}


        public void StartGraphDrawing()
        {
            //рисуем вершины графа
            
            foreach (INodeData nd in MainGraph.Nodes)
            {
                //Type tNode = nodeTypes.Find(delegate(Type t)
                //{
                //    return t.Name == nd.NType;
                //});
                _view.DrawNode(nd);
            }

            //рисуем дуги графа
            foreach (IEdgeData ed in MainGraph.Edges)
            {
                var sNode = MainGraph.Nodes.Find(iN => (iN.Id == ed.SourceNode));
                var tNode = MainGraph.Nodes.Find(iN => (iN.Id == ed.TargetNode));
                _view.DrawEdge(ed, sNode, tNode);
            }
        }

        //обновить представление
        public void RefreshGraphLayout()
        {
            foreach (INodeData nd in MainGraph.Nodes)
            {
                _view.RefreshNode(nd);
            }
            foreach (IEdgeData ed in MainGraph.Edges)
            {
                INodeData sNode = MainGraph.Nodes.Find(iN => (iN.Id == ed.SourceNode));
                INodeData tNode = MainGraph.Nodes.Find(iN => (iN.Id == ed.TargetNode));
                _view.RefreshEdge(ed, sNode, tNode);
            }
        }

        #region Методы для получения инфы о модели
       
        //получить вершину

        public INodeData GetNodeInfo(string ID)
        {
            return (MainGraph.GetNode(ID));
        }

        //получить дугу
        public IEdgeData GetEdgeInfo(string ID)
        {
            return (MainGraph.GetEdge(ID));
        }

        //получить коллекцию вершин
        public List<INodeData> GetNodesCollection()
        {
            return MainGraph.Nodes;
        }

        //получить коллекцию дуг
        public List<IEdgeData> GetEdgesCollection()
        {
            return MainGraph.Edges;
        }

        #endregion

        #region Методы для внешнего обновления модели

        public void SetMultySelect(bool MultySelect)
        {
            _view.SetMultySelect(MultySelect);
        }

        public void SelectNode(string nodeID)
        {
            _view.SelectNode(nodeID);
        }

        //обновить количество

        public void UpdateNodeCount(int count)
        {
            _view.InitializeView(count);
        }

        //добавление вершины к модели
        public void AddNodeToModel(INodeData node)
        {
            MainGraph.AddNode(node);
            _view.DrawNode(node);
        }
        //обновление вершины
        public void RefreshNodeInfo(string lastID, INodeData newNode)
        {
            var n = MainGraph.GetNode(lastID);
            var oldCoords = new Point {X = n.Pos.X, Y = n.Pos.Y};
            MainGraph.DeleteNode(n);
            _view.DeleteNode(n);
            newNode.Pos = oldCoords;
            MainGraph.AddNode(newNode);
            _view.DrawNode(newNode);
        }

        

        //удалить вершину из модели
        public void DeleteNodeFromModel(string nodeID)
        {
            INodeData node = MainGraph.GetNode(nodeID);
            _view.DeleteNode(node);
            //находим дуги иницидентные вершине и удаляем их
            List<IEdgeData> help = new List<IEdgeData>();//помешаем сюда вершины для удаления
            foreach (IEdgeData edge in MainGraph.Edges)
                if (edge.SourceNode == nodeID || edge.TargetNode == nodeID)
                {
                    _view.DeleteEdge(edge);
                    help.Add(edge);
                }
            foreach (IEdgeData Edge in help)
            {
                MainGraph.DeleteEdge(Edge);
            }
            MainGraph.DeleteNode(node);

        }



        //добавление ребра к модели - исправить
        public void AddEdgeToModel(IEdgeData edge)
        {
            
            INodeData sourceNode = MainGraph.GetNode(edge.SourceNode);
            INodeData targetNode = MainGraph.GetNode(edge.TargetNode);
            //находим все параллельные дуги
            List<IEdgeData> founded = MainGraph.Edges.FindAll(delegate(IEdgeData _edge)
            {
                return (_edge.SourceNode == edge.SourceNode && _edge.TargetNode == edge.TargetNode &&
                    _edge.EdgeText == edge.EdgeText);
            });
            MainGraph.AddEdge(edge);
            //если все как обычно
            if (edge.EdgeText != "sub" || (founded.Count == 0))
            {
                edge.Curve = EdgeType.Linear;
                _view.DrawEdge(edge, sourceNode, targetNode);
            }
            //если нужно рисовать паралельные дуги
            if (edge.EdgeText == "sub" && (founded.Count != 0))
            {
                //меняем тип дуги
                edge.Curve = EdgeType.Round;
                //производим вычисления
                if (founded.Count % 2 != 0)
                {
                    edge.IsTop = true;
                }
                else
                {
                    edge.IsTop = false;
                }
                edge.RadY = (founded.Count + 1) / 2;
                
                
                _view.DrawEdge(edge, sourceNode, targetNode);

            }
           
            
            
        }

        //удаление ребер из модели
        public void DeleteAllEdgesFromModel()
        {
            foreach (IEdgeData edge in MainGraph.Edges)
            {
                _view.DeleteEdge(edge);
            }
            MainGraph.Edges.Clear();
        }

        //удаление ребра
        public void DeleteEdge(string edgeID)
        {
            IEdgeData edge = MainGraph.GetEdge(edgeID);
            _view.DeleteEdge(edge);
            MainGraph.Edges.Remove(edge);
        }


        //удалить все вершины

        public void DeleteAllNodes()
        {
            foreach (var node in MainGraph.Nodes)
            {
                _view.DeleteNode(node);
            }
            MainGraph.Nodes.Clear();
            MainGraph.Edges.Clear();
        }

        
        #endregion 

        #region Обработка событий, поступающих от представления

        private void OnChangeNodePosition(Object sender, NodeMoveEventArgs e)
        {
            //обновляем координаты вершины
            var node = MainGraph.GetNode(e.Id);
            node.Pos = new Point(e.Point.X, e.Point.Y);
            _view.RefreshNode(node);
            //находим дуги инцидентные вершине и обновляем их
            foreach (var edge in MainGraph.Edges)
               if (edge.SourceNode == node.Id || edge.TargetNode == node.Id)
                {
                    var sNode = MainGraph.Nodes.Find(iN => (iN.Id == edge.SourceNode));
                    var tNode = MainGraph.Nodes.Find(iN => (iN.Id == edge.TargetNode));
                    _view.RefreshEdge(edge, sNode,tNode);
                }

        }


       
        //добавление новой вершины
        public void _view_AddNewNode(Object sender, NodeAddEventArgs e)
        {
           //INode n = classFactory.CreateNodeInstance(e.ID,e.NodeType);
           //MainGraph.AddNode(n);
           //_view.DrawNode(n);
            //теперь оповостим об этом внешнюю программу, использующую компонент
           if (OnAddNewNode != null)
               OnAddNewNode(this, e);
        }

        //добавление новой дуги
        //private void _view_AddNewEdge(Object sender, EdgeAddEventArgs e)
        //{
        //    INodeData sNode = MainGraph.Nodes.Find(delegate(INodeData iN)
        //    {
        //        return (iN.Id == e.sourceNodeId);
        //    });
        //    INodeData tNode = MainGraph.Nodes.Find(delegate(INodeData iN)
        //    {
        //        return (iN.Id == e.targetNodeId);
        //    });
        //    //добавляемая дуга
        //    IEdgeData edg = classFactory.CreateEdgeInstance(e.edgeID, sNode, tNode,e.text, e.typeName);
        //    //находим все параллельные дуги
        //    List<IEdgeData> founded = MainGraph.Edges.FindAll(delegate(IEdgeData _edge)
        //    {
        //        return (_edge.SourceNode == e.sourceNodeId && _edge.TargetNode == e.targetNodeId &&
        //            _edge.EdgeText == e.text);
        //    });

        //    //если все как обычно
        //    if (classFactory.UsualDrawing(MainGraph.Edges, MainGraph.Nodes, edg))
        //    {
        //        edg.radY = 0;
        //        MainGraph.AddEdge(edg);
        //        _view.RefreshEdge(edg, sNode, tNode);
        //        if (classFactory.CanAddEdge(MainGraph.Edges, MainGraph.Nodes, edg))
        //        {
        //            //оповещаем внешнее приложение 
        //            if (OnAddNewEdge != null)
        //                OnAddNewEdge(this, e);
        //        }
        //        else
        //        {
        //            _view.DeleteEdge(edg);
        //            MainGraph.DeleteEdge(edg);
        //            MessageBox.Show("Невозможно протянуть связь!!!");
        //        }
        //    }
        //    //если нужно рисовать паралельные дуги
        //    else 
        //    {
        //        //меняем тип дуги
        //        _view.DeleteEdge(edg);
        //        edg.curve = EdgeType.Round;
        //        MainGraph.AddEdge(edg);
        //        //оповещаем внешнее приложение
        //        if (OnAddNewEdge != null)
        //            OnAddNewEdge(this, e);
        //        //производим вычисления
        //        List<IEdgeData> subList = new List<IEdgeData>();
        //        bool b = true;
        //        int i = 0;
        //        while(b)
        //        {
        //            i++;
        //            subList = founded.FindAll(delegate(IEdgeData _edge)
        //            {
        //                return (_edge.radY == i);
        //            });
        //            if (subList.Count == 0)
        //            {
        //                b = false;
        //                edg.radY = i;
        //                edg.isTop = true;
        //            }
        //            if (subList.Count == 1)
        //            {
        //                b = false;
        //                edg.radY = i;
        //                edg.isTop = !subList[0].isTop;
        //            }
        //        }
        //        //рисуем параллельные
        //        _view.DrawEdge(edg, sNode, tNode);

        //    }
            
        //}
        
        //удаление дуги
        void _view_DeleteEdge(object sender, EdgeDeleteEventArgs e)
        {
            //запоминаем вершины, которые связывала дуга
            IEdgeData edge = MainGraph.GetEdge(e.EdgeId);
            e.SourceNodeId = edge.SourceNode;
            e.TargetNodeId = edge.TargetNode;
            //удаляем из модели данных и из представления
            _view.DeleteEdge(edge);
            MainGraph.DeleteEdge(edge);
            //оповещаем внешнее приложение
            onDeleteEdge(this, e);

        }

        //удаление вершин
        void _view_OnDeleteNode(object sender, NodeDeleteEventArgs e)
        {
            //INode node = MainGraph.GetNode(e.ID);
            ////удаляем вершину
            //_view.DeleteNode(node);
            ////находим дуги иницидентные вершине и удаляем их
            //List<IEdgeData> help = new List<IEdgeData>();//помешаем сюда дуги для удаления
            //foreach (IEdgeData edge in MainGraph.Edges)
            //    if (edge.SourceNode == node.id || edge.TargetNode == node.id)
            //    {
            //        _view.DeleteEdge(edge);
            //        help.Add(edge);
            //    }
            //foreach (IEdgeData Edge in help)
            //{
            //    MainGraph.DeleteEdge(Edge);
            //}
            //MainGraph.DeleteNode(node);
            //оповещаем внешнюю программу об удалении
            onDeleteNode(this, e);
        }


        void _view_OnSelectNode(object sender, NodeAddEventArgs e)
        {
            SelectedNodeId = e.Id;
            if (onSelectNode != null)
                onSelectNode(this, e);
        }

        void _view_OnChangeOperation(object sender, OperationEventArgs e)
        {
            if (OnChangeOperation != null)
                OnChangeOperation(this, e);
        }

        void _view_UpdateAllNodeEdges(object sender, NodeAddEventArgs e)
        {
            foreach (IEdgeData edge in MainGraph.Edges)
                if (edge.SourceNode == e.Id || edge.TargetNode == e.Id)
                {
                    INodeData sNode = MainGraph.Nodes.Find(delegate(INodeData iN)
                    {
                        return (iN.Id == edge.SourceNode);
                    });
                    INodeData tNode = MainGraph.Nodes.Find(delegate(INodeData iN)
                    {
                        return (iN.Id == edge.TargetNode);
                    });
                    _view.RefreshEdge(edge, sNode, tNode);
                }
        }

        void _view_OnDeselectAll(object sender, EventArgs e)
        {
            if (OnDeselectAll != null)
            {
                OnDeselectAll(this, new EventArgs());
            }
        }

        //при автоматической укладке
        void _view_OnAutomaticLayout(object sender, EventArgs e)
        {
            //вычислить координаты вершин уложенного графа
            MainGraph.AutomaticLayout();
            //обновить представление
            RefreshGraphLayout();
        }


        #endregion

    }
}
